/*
 * [y] hybris Platform
 *
 * Copyright (c) 2000-2017 SAP SE
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of SAP
 * Hybris ("Confidential Information"). You shall not disclose such
 * Confidential Information and shall use it only in accordance with the
 * terms of the license agreement you entered into with SAP Hybris.
 */
package acc.asset.sync;

import de.hybris.deltadetection.ChangeDetectionService;
import de.hybris.deltadetection.ItemChangeDTO;
import de.hybris.platform.core.GenericSearchConstants.LOG;
import de.hybris.platform.core.model.media.MediaModel;
import de.hybris.platform.servicelayer.media.MediaService;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.type.TypeService;
import de.hybris.platform.task.TaskService;
import de.hybris.platform.util.logging.Logs;
import de.hybris.y2ysync.enums.Y2YSyncType;
import de.hybris.y2ysync.model.media.ConsumeMarkerMediaModel;
import de.hybris.y2ysync.model.media.SyncImpExMediaModel;
import de.hybris.y2ysync.task.dao.Y2YSyncDAO;
import de.hybris.y2ysync.task.runner.TaskContext;
import de.hybris.y2ysync.task.runner.internal.ImportScript;
import de.hybris.y2ysync.task.runner.internal.ProcessChangesTask;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.springframework.util.SerializationUtils;

import com.google.common.collect.ImmutableSet;


/**
 *
 */
public class DemoProcessChangesTask extends ProcessChangesTask
{
	private static final Logger LOG = Logger.getLogger(ProcessChangesTask.class);
	private final ModelService modelService;
	private final MediaService mediaService;
	private final ChangeDetectionService changeDetectionService;
	private final TaskService taskService;
	private final TypeService typeService;
	private final Y2YSyncDAO y2YSyncDAO;
	private final List<ItemChangeDTO> changes;
	private final Collection<ImportScript> scripts;
	private final String conditionToTrigger;
	private final String syncExecutionId;
	private final String dataHubColumns;
	private final Y2YSyncType syncType;
	private final String dataHubType;

	public DemoProcessChangesTask(final ModelService modelService, final MediaService mediaService,
			final ChangeDetectionService changeDetectionService, final TaskService taskService, final TypeService typeService,
			final Y2YSyncDAO y2YSyncDAO, final TaskContext context, final Collection<ImportScript> importScripts)
	{

		super(modelService, mediaService, changeDetectionService, taskService, typeService, y2YSyncDAO, context, importScripts);

		this.modelService = Objects.requireNonNull(modelService, "modelService can't be null");
		this.mediaService = Objects.requireNonNull(mediaService, "mediaService can't be null");
		this.changeDetectionService = Objects.requireNonNull(changeDetectionService, "changeDetectionService can't be null");
		this.taskService = Objects.requireNonNull(taskService, "taskService can't be null");
		this.typeService = Objects.requireNonNull(typeService, "typeService can't be null");
		this.y2YSyncDAO = Objects.requireNonNull(y2YSyncDAO, "y2YSyncDAO can't be null");
		this.scripts = Objects.requireNonNull(importScripts, "imporScripts can't be null");
		Objects.requireNonNull(context, "task context can't be null");
		this.changes = context.getChanges();
		this.conditionToTrigger = context.getConditionToTrigger();
		this.syncExecutionId = context.getSyncExecutionId();
		this.dataHubColumns = context.getDataHubColumns();
		this.syncType = context.getSyncType();
		this.dataHubType = context.getDataHubType();
	}

	@Override
	public Collection<String> execute() throws Exception
	{
		final Collection<String> createdMediaCodes = this.createMediaForScripts();
		createdMediaCodes.forEach(mediaCode -> LOG.info("SyncImpExMediaModel code:" + mediaCode));
		this.consumeChangesOrCreateConsumeMarkers();
		this.triggerTaskCondition();
		return createdMediaCodes;
	}

	private Collection<String> createMediaForScripts()
	{
		final ImmutableSet.Builder resultBuilder = ImmutableSet.builder();
		for (final ImportScript script : this.scripts)
		{
			final SyncImpExMediaModel media = this.createMediaForScript(script);
			Logs.debug(LOG, () -> "Created SyncImpExMediaModel >>>> " + media + " [syncExecutionID: " + this.syncExecutionId + "]");
			resultBuilder.add(media.getCode());
		}
		return resultBuilder.build();
	}

	private void consumeChangesOrCreateConsumeMarkers()
	{
		if (this.changes == null || this.changes.isEmpty())
		{
			return;
		}

		if (this.syncType == Y2YSyncType.ZIP)
		{
			this.changeDetectionService.consumeChanges(this.changes);
		}
		else if (this.syncType == Y2YSyncType.DATAHUB)
		{
			this.createConsumeMarkerMedia();
		}
	}

	private ConsumeMarkerMediaModel createConsumeMarkerMedia()
	{
		final ConsumeMarkerMediaModel media = (ConsumeMarkerMediaModel) this.modelService.create(ConsumeMarkerMediaModel.class);
		media.setCode(UUID.randomUUID().toString());
		media.setSyncExecutionID(this.syncExecutionId);
		this.modelService.save(media);
		this.mediaService.setDataForMedia(media, SerializationUtils.serialize(this.changes));
		return media;
	}

	private void triggerTaskCondition()
	{
		this.taskService.triggerEvent(this.conditionToTrigger);
	}

	private SyncImpExMediaModel createMediaForScript(final ImportScript script)
	{
		LOG.info("create media for type:" + script.getTypeCode());
		LOG.info("script header:" + script.getHeader());
		LOG.info("script header length:" + script.getHeader().length());
		final SyncImpExMediaModel media = this.modelService.create(SyncImpExMediaModel.class);
		final String code = String.valueOf(script.getTypeCode()) + "-" + UUID.randomUUID().toString();
		media.setCode(code);
		media.setMime("text/plain");
		media.setRealFileName(code);
		media.setImpexHeader(script.getHeader());
		media.setExportCronJob(this.y2YSyncDAO.findSyncCronJobByCode(this.syncExecutionId));
		media.setDataHubColumns(this.dataHubColumns);
		media.setDataHubType(this.dataHubType);
		media.setSyncType(this.typeService.getComposedTypeForCode(script.getTypeCode()));
		media.setMediaArchive(
				script.getMediaArchivePk() == null ? null : (MediaModel) this.modelService.get(script.getMediaArchivePk()));
		this.modelService.save(media);
		this.mediaService.setDataForMedia(media, script.getContent().getBytes());
		return media;
	}
}
